package cn.dxs.adt.utils;

import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Build;
import android.text.TextUtils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.attribute.FileTime;
import java.security.CodeSigner;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import axmlprinter2.AXMLPrinter;
import cn.dxs.adt.App;
import cn.dxs.adt.bean.AppInfo;

public class SigUtil {

    public static Signature getSignature(String pkg) {
        try {
            PackageInfo packageInfo = App.getContext().getPackageManager().getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
            if (packageInfo.signatures != null) {
                return packageInfo.signatures[0];
            }
        } catch (Exception e2) {
        }
        return null;
    }

    public static String getSig(Signature sig) {
        if (sig == null) {
            return "";
        }
        return sig.toCharsString();
    }

    private static String getSig(Signature sig, String algorithm) {
        try {
            byte[] digest = MessageDigest.getInstance(algorithm).digest(sig.toByteArray());
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(Integer.toHexString((b & 255) | 256).substring(1, 3)).append(":");
            }
            return sb.substring(0, sb.length() - 1).toUpperCase();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static String getMD5(Signature sig) {
        return getSig(sig, "MD5");
    }

    public static String getSHA1(Signature sig) {
        return getSig(sig, "SHA1");
    }

    public static String getSHA256(Signature sig) {
        return getSig(sig, "SHA256");
    }


    /**
     * 从APK中读取 AndroidManifest.xml
     *
     * @param appInfo
     */
    public static void getSignaturesFromApk(AppInfo appInfo) {
        JarFile jarFile = null;
        try {
            jarFile = new JarFile(appInfo.apkPath);
            JarEntry je = jarFile.getJarEntry("AndroidManifest.xml");
            XmlPullParser parser = AXMLPrinter.getAndroidManifest(jarFile.getInputStream(je));

            if (parser == null) {
                return;
            }
            String namespace = "http://schemas.android.com/apk/res/android";
            for (int type = parser.getEventType(); type != XmlPullParser.END_DOCUMENT; type = parser.next()) {
                if (type == XmlPullParser.START_TAG) {
                    String name = parser.getName();
                    if ("manifest".equals(name)) {
                        /*<manifest
                        xmlns:android="http://schemas.android.com/apk/res/android"
                        android:versionCode="1086"
                        android:versionName="1.086"
                        android:installLocation="0"
                        android:compileSdkVersion="23"
                        android:compileSdkVersionCodename="6.0-2438415"
	                    package="com.lwhy.dbxgs"
                        platformBuildVersionCode="1086"
                        platformBuildVersionName="1.086"
                        >*/
                        int attributeCount = parser.getAttributeCount();
                        for (int i = 0; i < attributeCount; i++) {
                            String attributeName = parser.getAttributeName(i);
                            String attributeNamespace = parser.getAttributeNamespace(i);
                            if (!TextUtils.isEmpty(attributeNamespace)) {
                                namespace = attributeNamespace;
                            }
                            String attributePrefix = parser.getAttributePrefix(i);
                            String attributeType = parser.getAttributeType(i);
                            String attributeValue = parser.getAttributeValue(i);
                            String a = parser.getAttributeValue(attributeNamespace, attributeName);

                            if ("versionCode".equals(attributeName)) {
                            } else if ("versionName".equals(attributeName)) {
                            } else if ("installLocation".equals(attributeName)) {
                            } else if ("compileSdkVersion".equals(attributeName)) {
                            } else if ("compileSdkVersionCodename".equals(attributeName)) {
                            } else if ("package".equals(attributeName)) {
                            } else if ("platformBuildVersionCode".equals(attributeName)) {
                            } else if ("platformBuildVersionName".equals(attributeName)) {
                            }
                        }

                    } else if ("uses-sdk".equals(name)) {
                        // <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"></uses-feature>
                        String attributeValue1 = parser.getAttributeValue(namespace, "minSdkVersion");
                        String attributeValue2 = parser.getAttributeValue(namespace, "targetSdkVersion");
                        if (!TextUtils.isEmpty(attributeValue1)) {
                            appInfo.minSdkVersion = attributeValue1;
                        }
                        if (!TextUtils.isEmpty(attributeValue2)) {
                            appInfo.targetSdkVersion = attributeValue2;
                        }
                    } else if ("uses-feature".equals(name)) {
                        // <uses-feature android:glEsVersion="0x00020000"></uses-feature>
                    } else if ("uses-permission".equals(name)) {
                        // <uses-permission android:name="android.permission.INTERNET"></uses-permission>
                        appInfo.requestedPermissionInfo.add(parser.getAttributeValue(namespace, "name"));
                    } else if ("permission".equals(name)) {
                        // <permission android:name="getui.permission.GetuiService.com.lwhy.dbxgs" android:protectionLevel="0x00000000"></permission>
                    } else if ("application".equals(name)) {
                        /*<application
                            android:theme="@style/Theme.MyApplication"
                            android:label="@7F0E003D"
                            android:icon="@7F0C0000"
                            android:name="cn.dxs.myapplication.App"
                            android:exported="true"
                            android:allowBackup="true"
                            android:supportsRtl="true"
                            android:roundIcon="@mipmap/ic_launcher_round"
                            android:usesCleartextTraffic="true"
                            android:networkSecurityConfig="@7F11000C"
                            android:appComponentFactory="android.support.v4.app.CoreComponentFactory"
                            >*/

                        int attributeCount = parser.getAttributeCount();
                        for (int i = 0; i < attributeCount; i++) {
                            String attributeName = parser.getAttributeName(i);
                            String attributeNamespace = parser.getAttributeNamespace(i);
                            String attributePrefix = parser.getAttributePrefix(i);
                            String attributeType = parser.getAttributeType(i);
                            String attributeValue = parser.getAttributeValue(i);
                            String a = parser.getAttributeValue(attributeNamespace, attributeName);

                            if ("theme".equals(attributeName)) {
                            } else if ("label".equals(attributeName)) {
                            } else if ("icon".equals(attributeName)) {
                            } else if ("name".equals(attributeName)) {
                            } else if ("exported".equals(attributeName)) {
                            } else if ("allowBackup".equals(attributeName)) {
                            } else if ("supportsRtl".equals(attributeName)) {
                            } else if ("roundIcon".equals(attributeName)) {
                            } else if ("usesCleartextTraffic".equals(attributeName)) {
                            } else if ("networkSecurityConfig".equals(attributeName)) {
                            } else if ("appComponentFactory".equals(attributeName)) {
                            }
                        }

                    } else if ("meta-data".equals(name)) {
                        // <meta-data android:name="android.app.lib_name" android:value="cocos2djs"/>
                        int attributeCount = parser.getAttributeCount();
                        if (attributeCount >= 2) {
                            String attributeValue1 = parser.getAttributeValue(namespace, "name");
                            String attributeValue2 = parser.getAttributeValue(namespace, "value");
                            if (!TextUtils.isEmpty(attributeValue1) && !TextUtils.isEmpty(attributeValue2)) {
                                appInfo.metaData.put(attributeValue1, attributeValue2);
                                if ("android.app.lib_name".equals(attributeValue1) && "cocos2djs".equals(attributeValue2)) {
                                    appInfo.isCocos2djs = true;
                                }
                            }
                        }
                    } else if ("activity".equals(name)) {
                        /*<activity android:configChanges="keyboardHidden|orientation|screenSize" android:name="com.lwhy.dbxgs.SplashActivity" android:screenOrientation="portrait" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
                            <intent-filter>
                                <action android:name="android.intent.action.MAIN"/>
                                <category android:name="android.intent.category.LAUNCHER"/>
                            </intent-filter>
                        </activity>
                        <activity android:launchMode="singleTask" android:name="com.tencent.tauth.AuthActivity" android:noHistory="true">
                            <intent-filter>
                                <action android:name="android.intent.action.VIEW"/>
                                <category android:name="android.intent.category.DEFAULT"/>
                                <category android:name="android.intent.category.BROWSABLE"/>
                                <data android:scheme="tencent101833275"/>
                            </intent-filter>
                            <intent-filter>
                                <action android:name="android.intent.action.VIEW"/>
                                <category android:name="android.intent.category.DEFAULT"/>
                                <category android:name="android.intent.category.BROWSABLE"/>
                                <data android:scheme="tencent101885818"/>
                            </intent-filter>
                        </activity>*/
                        appInfo.activityInfo.add(parser.getAttributeValue(namespace, "name"));
                    } else if ("provider".equals(name)) {
                        /*<provider android:authorities="com.lwhy.dbxgs.TTFileProvider" android:exported="false" android:grantUriPermissions="true" android:name="com.bytedance.sdk.openadsdk.TTFileProvider">
                            <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/anythink_bk_tt_file_path"/>
                        </provider>*/
                        appInfo.providerInfo.add(parser.getAttributeValue(namespace, "name"));
                    } else if ("receiver".equals(name)) {
                        /*<receiver android:name="com.igexin.sdk.PushReceiver">
                            <intent-filter>
                                <action android:name="android.intent.action.BOOT_COMPLETED"/>
                                <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
                                <action android:name="android.intent.action.USER_PRESENT"/>
                                <action android:name="android.intent.action.MEDIA_MOUNTED"/>
                                <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
                                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
                            </intent-filter>
                        </receiver>*/
                        appInfo.receiverInfo.add(parser.getAttributeValue(namespace, "name"));
                    } else if ("service".equals(name)) {
                        /*<service android:exported="false" android:name="com.ss.android.socialbase.downloader.downloader.IndependentProcessDownloadService" android:process=":downloader">
                            <intent-filter>
                                <action android:name="com.ss.android.socialbase.downloader.remote"/>
                            </intent-filter>
                        </service>*/
                        appInfo.serviceInfo.add(parser.getAttributeValue(namespace, "name"));
                    } else if ("uses-library".equals(name)) {
                        // <uses-library android:name="org.apache.http.legacy" android:required="false"/>
                        int attributeCount = parser.getAttributeCount();
                        if (attributeCount >= 2) {
                            String attributeValue1 = parser.getAttributeValue(namespace, "name");
                            String attributeValue2 = parser.getAttributeValue(namespace, "required");
                            if (!TextUtils.isEmpty(attributeValue1) && !TextUtils.isEmpty(attributeValue2)) {
                                appInfo.usesLibrary.put(attributeValue1, attributeValue2);
                            }
                        }
                    }
                }
            }


        } catch (IOException e) {
            e.printStackTrace();
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } finally {
            if (jarFile != null) {
                try {
                    jarFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 从APK中读取签名
     *
     * @param file
     * @return
     * @throws IOException
     */
    public static List<String> getSignaturesFromApk2(String file) {
        List<String> signatures = new ArrayList<String>();
        try {
            JarFile jarFile = new JarFile(file);
            Enumeration<JarEntry> e = jarFile.entries();
            Map<String, JarEntry> map = new HashMap<>();
            while (e.hasMoreElements()) {
                JarEntry entry = e.nextElement();
                L.d("getSignaturesFromApk", entry.getName());
                map.put(entry.getName(), entry);
            }


            JarEntry je = jarFile.getJarEntry("AndroidManifest.xml");
            byte[] readBuffer = new byte[8192];
            Certificate[] certs = loadCertificates(jarFile, je, readBuffer);
            byte[] extra = je.getExtra();
            String comment = je.getComment();
            Attributes attributes = je.getAttributes();
            CodeSigner[] codeSigners = je.getCodeSigners();
            long cSize = je.getCompressedSize();
            long crc = je.getCrc();
            int method = je.getMethod();
            String name = je.getName();
            long size = je.getSize();
            long time = je.getTime();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                FileTime cTime = je.getCreationTime();
                FileTime laTime = je.getLastAccessTime();
                FileTime lmTime = je.getLastModifiedTime();
                String tos = cTime.toString();
            }
            if (certs != null) {
                for (Certificate c : certs) {
                    String sig = toCharsString(c.getEncoded());
                    signatures.add(sig);
                }
            }
        } catch (Exception ex) {
        }
        return signatures;
    }

    /**
     * 加载签名
     *
     * @param jarFile
     * @param je
     * @param readBuffer
     * @return
     */
    private static Certificate[] loadCertificates(JarFile jarFile, JarEntry je, byte[] readBuffer) {
        try {
            InputStream is = jarFile.getInputStream(je);
            while (is.read(readBuffer, 0, readBuffer.length) != -1) {
            }
            is.close();
            return je != null ? je.getCertificates() : null;
        } catch (IOException e) {
        }
        return null;
    }

    /**
     * 将签名转成转成可见字符串
     *
     * @param sigBytes
     * @return
     */
    private static String toCharsString(byte[] sigBytes) {
        byte[] sig = sigBytes;
        final int N = sig.length;
        final int N2 = N * 2;
        char[] text = new char[N2];
        for (int j = 0; j < N; j++) {
            byte v = sig[j];
            int d = (v >> 4) & 0xf;
            text[j * 2] = (char) (d >= 10 ? ('a' + d - 10) : ('0' + d));
            d = v & 0xf;
            text[j * 2 + 1] = (char) (d >= 10 ? ('a' + d - 10) : ('0' + d));
        }
        return new String(text);
    }

}
